package com.edouardbataille.open.handler
{
	import __AS3__.vec.Vector;
	
	import com.edouardbataille.open.MySqlSocket;
	import com.edouardbataille.open.MysqlConst;
	import com.edouardbataille.open.bytes.ByteReader;
	import com.edouardbataille.open.result.Result;
	import com.edouardbataille.open.result.ResultSet;
	import com.edouardbataille.open.result.SqlMetadata;
	
	import mx.rpc.Fault;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

	public class QueryResultHandler extends AbstractHandler implements IHandler
	{
		private function readHeader (data : ByteReader) : void
		{
			var length : uint = data.readPacketLength();
			var packet : uint = data.readPacketNum();
			fieldCount= data.readLengthCode();
			lastResult = new ResultSet (fieldCount);
		}

		public function handleData(packets:Vector.<ByteReader>, con:MySqlSocket):void
		{
			var data : ByteReader;
			if (!header)
			{
				header = true;
				data = packets.shift ();
				readHeader (data);
			
				if (fieldCount == 0xFF)
				{
					var fault : Fault = FaultParser.parseFault(data);
					dispatchEvent(FaultEvent.createEvent(fault));
					return;
				}
				
				if (fieldCount == 0)
				{
					var affectedRow : uint = data.readByte();
					var insert_id : uint = data.readLengthCode();
					var serverStatus : uint = data.readUShort();
					var warning : uint = data.readUShort();
					lastResult.lastInsertId = insert_id;
					dispatchEvent(ResultEvent.createEvent(lastResult));
					return;
				}
			}

			for (; fieldCountRead < fieldCount; ++fieldCountRead)
			{
				data = packets.shift ();
				var length : uint = data.readPacketLength();
				var packet : uint = data.readPacketNum();
				readField (data, fieldCountRead);
			}
			if (!firstEOFReaded)
			{
				data = packets.shift ();
				if (!readEOF(data))
					return; //TODO change by throw Error ();
				else
					firstEOFReaded = true;
			}
			
			while (!secondEOFReaded && packets.length > 0)
			{
				data = packets.shift ();
				if (readEOF(data))
					secondEOFReaded = true;
				else
				{
					var rowLength : uint = data.readPacketLength();
					var rowPacket : uint = data.readPacketNum();
					readRow (data);
				}
			}
			if (secondEOFReaded)
				dispatchEvent(ResultEvent.createEvent(lastResult));
		}
		
		private function readEOF (data : ByteReader) : Boolean
		{
			var reading : uint = 4;
			var length : uint = data.readPacketLength();
			var packet : uint = data.readPacketNum();
			if (length == 5)
			{
				reading = 5;
				var value : uint = data.readByte() & 0xFF;
				if (value == 0xFE)
				{
					data.position += 4;
					return true;
				}
			}
			data.position -= reading;
			return false;
		}
		
		private function readField (data:ByteReader, pos : int) : void
		{
			var catalog : String = data.readLenString();
			var db : String = data.readLenString();
			var table : String = data.readLenString();
			var org_table : String = data.readLenString();
			var name : String = data.readLenString();
			var org_name : String = data.readLenString();
			var filler : uint = data.readByte();		// 1
			var charSet : uint = data.readUShort();		// 2
			var lengthField : int = data.readMultipleByte(4);		// 4
			var type : uint = data.readByte() & 0xFF;			// 1
			var flag : uint = data.readUShort();			// 2
			var decimal : int = data.readByte() & 0xFF;		// 1
			filler = data.readShort();					// 2;
			var defaultValue : uint = data.readMultipleByte(data.bytesAvailable);
			
			var metaData : SqlMetadata = new SqlMetadata (catalog, db, table, org_table, name, org_name, lengthField, type, flag, decimal);
			lastResult.addMeta (metaData, pos);
		}
		
		private function readRow (data : ByteReader) : void
		{
			while (data.bytesAvailable)
			{
				var row : Object = new Object ();
				var arr : Array = new Array ();
				var strValue : String;
				var date : Date;
				for each (var meta : SqlMetadata in lastResult.metaDatas)
				{
					if (row.hasOwnProperty(meta.column))
						trace ("WARNING you have two vars wich have same name ("+ meta.column + ") don't use Object 'result'");
					strValue = data.readLenString();
					if (!strValue)
					{
						row[meta.column] = null;
						arr.push(null);
						continue;
					}
					switch (meta.type)
					{
						case MysqlConst.FIELD_TYPE_LONGLONG:
							var longlong : uint = parseInt(strValue);
							row[meta.column] = longlong;
							arr.push(longlong);
							break;
						case MysqlConst.FIELD_TYPE_LONG:
							var long : int = parseInt(strValue)
							row[meta.column] = long;
							arr.push(long);
							break;
						case MysqlConst.FIELD_TYPE_VAR_STRING:
						case MysqlConst.FIELD_TYPE_VARCHAR:
							row[meta.column] = strValue;
							arr.push(strValue);
							break;
						case MysqlConst.FIELD_TYPE_TINY:
							var tiny : int = parseInt(strValue);
							row[meta.column] = tiny;
							arr.push(tiny);
							break;
						case MysqlConst.FIELD_TYPE_DATE:
							date = readDate(strValue);
							row[meta.column] = date;
							arr.push(date);
							break;
						case MysqlConst.FIELD_TYPE_DATETIME:
							var dh : Array = strValue.split(" ");
							date = readDate(dh[0]);
							date = readTime(dh[1], date);
							row[meta.column] = date;
							arr.push(date);
							break;
						case MysqlConst.FIELD_TYPE_TIME:
							date = readTime(strValue);
							row[meta.column] = date;
							arr.push(date);
							break;
					}
				}
				var res : Result = new Result ();
				res.result = row;
				res.array = arr;
				lastResult.addRow(res);
			}
		}
		
		private function readDate (value : String, date : Date=null) : Date
		{
			if (!date)
				date = new Date ();
			var values : Array = value.split("-");
			date.setFullYear(values[0], values[1], values[2]);
			date.setHours(0, 0, 0, 0);
			return date;
		}
		
		private function readTime (value : String, date : Date=null) : Date
		{
			var timeValues : Array = value.split(":");
			if (!date)
				date = new Date ();
			date.setHours(timeValues[0], timeValues[1], timeValues[2], 0);
			return date;
		}
		
	}
}